﻿using System;
using System.Collections.Generic;
using System.Threading;
using log4net;

namespace ActionLogic
{
    /// <summary>
    /// Action executor state.
    /// </summary>
    public enum ActionExecutorState
    {
        Start,
        Pause,
        Stop
    }

    /// <summary>
    /// Describes implementation of action executor.
    /// </summary>
    public class ActionExecutor : IActionExecutor
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(ActionExecutor));

        private static readonly ActionExecutor _instance = new ActionExecutor();

        private readonly List<IExecutable> _actionQueue;

        public ActionExecutorState ActionExecutorState { get; private set; }
        public Thread CurrentThread { get; set; }

        private ActionExecutor()
        {
            _actionQueue = new List<IExecutable>();
            ActionExecutorState = ActionExecutorState.Stop;
            ThreadPool.SetMaxThreads(4, 4);
        }

        /// <summary>
        /// Returns instance of <code>ActionExecutor</code>
        /// </summary>
        /// <returns></returns>
        public static IActionExecutor GetInstance()
        {
            return _instance;
        }


        #region Lifecycle
        public void Start()
        {
            ActionExecutorState = ActionExecutorState.Start;
            logger.Info("START \n\n***START***\n");

            while (ActionExecutorState.Start == ActionExecutorState)
            {
                lock (this)
                {
                    Execute();
                }
            }
        }

        public void Pause()
        {
            ActionExecutorState = ActionExecutorState.Pause;

            logger.Info("PAUSE\n\n*** PAUSE ***\n");
        }

        public void Stop()
        {
            ActionExecutorState = ActionExecutorState.Stop;

            lock (this)
            {
                _actionQueue.Clear();

            }
            
            logger.Info("STOP\n\n*** STOP ***");
            logger.Info("ActionQueue count = " + _actionQueue.Count);
        }
        #endregion

        public void Add(IExecutable executable)
        {
            _actionQueue.Add(executable);

            logger.Info("ADD: " + executable + "; prior = " + executable.Priority);
        }

        public void Execute()
        {
            if (ActionExecutorState != ActionExecutorState.Start)
            {
                return;
            }

            if (IsEmpty())
            {
                logger.Error("EMPTY ACTION QUEUE!");
                return;
            }

            //TODO: FIX sort
            _actionQueue.Sort((a, b) => a.Priority.CompareTo(b.Priority));
            var maxPriorityAction = _actionQueue[0];
            if (maxPriorityAction.IsBackground)
            {
                ThreadPool.QueueUserWorkItem(context => maxPriorityAction.Execute());
            }
            else
            {
                maxPriorityAction.Execute();                
            }

            _actionQueue.Remove(maxPriorityAction);
        }

        public bool IsEmpty()
        {
            return _actionQueue.Count == 0;
        }
    }
}
